#pragma once

#include <iostream>
#include <string>
#include <pthread.h>
#include <unordered_map>
#include <vector>
#include<sstream>
#include "Sock.hpp"
#include "log.hpp"
#include <fstream>
#include <unistd.h>

const std::string wwwroot = "./wwwroot";    //web根目录
const std::string sep = "\r\n";
const std::string homepage = "index.html";

static const int defaultport = 8080;

class HttpServer;

class ThreadData
{
public:
    ThreadData(int fd, HttpServer *s) : sockfd(fd), svr(s)
    {
    }

public:
    int sockfd;
    HttpServer *svr;
};

class HttpRequest
{
public:
    void Deserialize(std::string req)
    {
        while(true)
        {
            std::size_t pos = req.find(sep);
            if(pos == std::string::npos) break;
            std::string temp = req.substr(0, pos);   //一整行被截下来了
            if(temp.empty())  break;    //此时是空行，说明下面是文本行了, 不截取呢
            req_header.push_back(temp);
            req.erase(pos, pos+sep.size());    //  讲 \r\n删掉
        }
        text = req;       //文本内容被存起来了
    }

    // .png:image.png
    void Parse()
    {
        std::stringstream ss(req_header[0]);   //流式提取
        ss>>method>>url>>http_version;
        file_path = wwwroot;
        if(url == "/" || url == "./index.html")
        {
            file_path+="/";
            file_path+=homepage;

        }
        else file_path+=url;   //  /a/b/c/d.html -> ./wwwroot/a/b/c/d.html
        
        auto pos = file_path.rfind(".");
        if(pos == std::string::npos) suffix = ".html";
        else suffix = file_path.substr(pos);     //获取文键后缀

    }


    void DebugPrint()
    {
        for(auto &line : req_header)
        {
            std::cout<< "-----------------------"<<std::endl;
            std::cout<<line<<"\n\n";
        }
        std::cout<<"method: "<<method<<std::endl;
        std::cout<<"url: "<<url<<std::endl;
        std::cout<<"http_version: "<<http_version<<std::endl;
        std::cout<<"file_path"<<file_path<<std::endl;

        std::cout<<text<<std::endl;
    }

public:
    std::vector<std::string> req_header;   //头部
    std::string text;    // 文本内容

    // 解析之后的结果
    std::string method;
    std::string url;
    std::string http_version;
    std::string file_path; //  ./wwwroot/a/b/c.html 2.png

    std::string suffix;     //文件后缀
};

class HttpServer
{
public:
    HttpServer(uint16_t port = defaultport) : port_(port)
    {
        content_type.insert({".html", "text/html"});
        content_type.insert({".png", "image/png"});   //content-Type 映射表
    }

    bool Start()
    {
        listensock_ = sock_.Socket();

        sock_.Bind(listensock_, port_);
        sock_.Listen(listensock_);
        for (;;)
        {
            std::string clientip;
            uint16_t clientport;
            int sockfd = sock_.Accept(listensock_, &clientip, &clientport);
            if (sockfd < 0)
                continue;
            logMessage(NORMAL, "get a new connect, sockfd: %d", sockfd);

            pthread_t tid;
            ThreadData *td = new ThreadData(sockfd, this);
            td->sockfd = sockfd;
            pthread_create(&tid, nullptr, ThreadRun, td);
        }
    }

    static std::string ReadHtmlContent(const std::string &htmlpath)
    {
        //坑
        //这种方法默认按照字符串读，但是文本内容没问题，如果是图片的话，图片是二进制内容，再用字符串读，会把字符串读漏掉
        std::ifstream in(htmlpath, std::ios::binary);    //以二进制模式打开文件流，这样不会改变源内容！
        if(!in.is_open()) return "";

        in.seekg(0, std::ios_base::end);   //将文件流指针设置在结尾
        auto len = in.tellg();             //获取文件大小，因为指针被设置在了末尾，因此返回的pos位置，就是文件大小
        in.seekg(0, std::ios_base::beg);    // 设置在开头
        

        std::string content;
        content.resize(len);

        in.read((char*)content.c_str(), content.size());   

        //这个也是一种读取方法，但是是按i字符串读的，只能读文本
        //std::string content;
        // std::string line;
        // while(std::getline(in, line))
        // {
        //     content += line;
        // }

        in.close();
        return content;
    }

    std::string SuffixToDesc(const std::string &suffix)
    {
        auto iter = content_type.find(suffix);
        if(iter == content_type.end()) return content_type[".html"];
        else return content_type[suffix];
    }

    void HandlerHttp(int sockfd)
    {
        char buffer[10240];
        ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);   //bug
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << buffer<<std::endl;   //假设我们读取到的是一个完整的，独立的http  请求
            HttpRequest req;
            req.Deserialize(buffer);
            req.Parse();    //提取报头和文本

            //先弄这个！
            // req.DebugPrint();

            //std::string path = wwwroot;
            //path +=url;  //  wwwroot/a/a/b/index.html

            //返回响应的过程
            std::string text;
            bool ok = true;
            text = ReadHtmlContent(req.file_path);  //读取网页内容

            if(text.empty())
            {
                ok = false;
                std::string err_html = wwwroot;
                err_html += "/";
                err_html +="err.html";
                text = ReadHtmlContent(err_html);
            }

            std::string respones_line;
            if(ok) respones_line = "HTTP/1.0 200 OK\r\n";
            else respones_line = "HTTP/1.0 404 Not Found\r\n";     //如果读取错误就吧404返回

            // respones_line = "HTTP/1.0 302 Found\r\n";    302重定向效果，+qq那行
            std::string response_header = "Content-Length: ";
            response_header += std::to_string(text.size());   //Content-Length: 11
            response_header +="\r\n";
            response_header+=SuffixToDesc(req.suffix);    //把响应的后缀转成指定描述内容
            response_header += "\r\n";
            response_header += "Set-Cookie: name=haha&&passwd=12345";    //测试Cookie的文件
            response_header += "\r\n";

            // response_header +="Location:https://www.qq.com\r\n";
            std::string blank_line = "\r\n";

            std::string response = respones_line;
            response +=response_header;
            response +=blank_line;
            response +=text;

            send(sockfd, response.c_str(), response.size(), 0);
        }

        close(sockfd);
    }

    static void *ThreadRun(void *args)
    {
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData *>(args);

        td->svr->HandlerHttp(td->sockfd);

        delete td;
        return nullptr;
    }

    ~HttpServer() {}

private:
    int listensock_;
    Sock sock_;
    uint16_t port_;

    std::unordered_map<std::string, std::string> content_type;
};